home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 41 / Amiga Format CD41 (1999-06)(Future Publishing)(GB)[!][issue 1999-07].iso / -seriously_amiga- / misc / wordwrap / wordwrap.c < prev    next >
C/C++ Source or Header  |  1999-04-19  |  24KB  |  695 lines

  1. /*-----------------------------------------------------------------------*\
  2.  |                            File: wordwrap.c                           |
  3.  |              Author: Wilhelm Nöker <wnoeker@t-online.de>              |
  4.  *-----------------------------------------------------------------------*
  5.  |  Reformat a text on the input stream to a given maximum line length.  |
  6.  |         Lots of command line options, see help() for details.         |
  7.  |                                                                       |
  8. \*-----------------------------------------------------------------------*/
  9.  
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <stdlib.h>
  13. #include <ctype.h>
  14.  
  15. char version[] = "$VER: wordwrap 2.3 (30.03.99)";
  16.  
  17. #define WORDLEN 200   /* max. size of an input word */
  18. #define KEYLEN   40   /* max. size of keywords (for -e/-aA/-zZ) */
  19. #define NDELIM   20   /* max. number of delimiter words */
  20. #define NKEEP   100   /* max. number of escaped paragraphs */
  21. typedef unsigned char UBYTE;   /* will make the lookup tables work better */
  22.  
  23.  
  24. /* lookup tables: */
  25. int is_graph[ 256 ], is_alnum[ 256 ], is_lower[ 256 ], is_cons[ 256 ];
  26.  
  27. /* adjustable parameters: */
  28. UBYTE solword[ NDELIM ][ KEYLEN ], eolword[ NDELIM ][ KEYLEN ];
  29. int scmplen[ NDELIM ], ecmplen[ NDELIM ];
  30. int eolwords = 0, solwords = 0;
  31. int keeppar[ NKEEP ];
  32. int keeppars = 0;
  33. int lmax = 75, shortchars = 0, indent = 0, blank2i = 0, tabsize = 4;
  34. unsigned int imargin = 0, omargin = 0;
  35.  
  36. /* switches: */
  37. int blanks = 0, i2blank = 0, smart_b2i = 0;
  38. int widespace = 0, unhyphen = 0, wrapnbsp = 0;
  39.  
  40.  
  41. void init_tabs();
  42. void wordwrap();
  43. int getword(UBYTE s[], int lim);
  44. void scrollmode( int imrg, int omrg );
  45.  
  46.  
  47. void help( UBYTE *s )
  48. /* print a help text and nag about illegal parameter <s> */
  49.     {
  50.     if( s )
  51.         fprintf( stderr, "illegal option '%s'\n", s );
  52.     fprintf( stderr, "'wordwrap' command line parameters:\n");
  53.     fprintf( stderr, " -l<len>                line length, defaults to 75\n");
  54.     fprintf( stderr, " -b / -bc[<indent>]     protect blank lines / convert to indentation\n");
  55.     fprintf( stderr, " -bC[<indent>]          like -bc, but convert single blank lines only\n");
  56.     fprintf( stderr, " -i / -i<indent> / -ic  protect indentation / convert to blank lines\n");
  57.     fprintf( stderr, " -ia / -ia<indent>      add blank lines before indentations\n");
  58.     fprintf( stderr, " -t<tabsize>            how to expand tab indentations\n");
  59.     fprintf( stderr, " -m<width> / -M<width>  add / strip left margin\n");
  60.     fprintf( stderr, " -s<len>                protect lines shorter than <len>\n");
  61.     fprintf( stderr, " -e<parno>              exempt a paragraph from reformatting\n");
  62.     fprintf( stderr, " -h / -H                undo hyphenation (preserving hyphens)\n");
  63.     fprintf( stderr, " -w / -W                wide spaces after '.!?' (and ':')\n");
  64.     fprintf( stderr, " -n                     make non-breakable space breakable\n");
  65.     fprintf( stderr, " -a<sstr> / -A<sword>   substring / word always starting a new line\n");
  66.     fprintf( stderr, " -z<estr> / -Z<eword>   substring / word always ending a line\n");
  67.     }
  68.  
  69.  
  70. int main( int argc, char* argv[] )
  71. /* command line parsing */
  72.     {
  73.     UBYTE *s;
  74.  
  75.     init_tabs();
  76.     while( --argc )
  77.         {
  78.         s = *++argv;
  79.         if( *s++ == '-' )
  80.             {
  81.             switch( *s++ )
  82.                 {
  83.                 case 'l':
  84.                     lmax = atoi( s );
  85.                     break;
  86.                 case 's':
  87.                     shortchars = atoi( s );
  88.                     break;
  89.                 case 'n':
  90.                     wrapnbsp = 1;
  91.                     break;
  92.                 case 'h':
  93.                     unhyphen = 1;
  94.                     break;
  95.                 case 'H':
  96.                     unhyphen = 2;
  97.                     break;
  98.                 case 'w':
  99.                     widespace = 1;
  100.                     break;
  101.                 case 'W':
  102.                     widespace = 2;
  103.                     break;
  104.                 case 'm':
  105.                     omargin = atoi(s);
  106.                     break;
  107.                 case 'M':
  108.                     imargin = atoi(s);
  109.                     break;
  110.                 case 't':
  111.                     tabsize = atoi(s);
  112.                     break;
  113.                 case 'b':
  114.                     blanks = 1;
  115.                     if( *s == 'c' || (smart_b2i = *s == 'C'))
  116.                         {
  117.                         blank2i = -1;   /* will be replaced by a value >0 later */
  118.                         if( isdigit( *++s ) )
  119.                             blank2i = atoi( s );
  120.                         }
  121.                     break;
  122.                 case 'i':
  123.                     indent = -1;        /* indent by original width */
  124.                     if( *s == 'c' )
  125.                         {
  126.                         indent = 0;     /* don't indent */
  127.                         i2blank = 1;
  128.                         }
  129.                     if( *s == 'a' )
  130.                         {
  131.                         i2blank = 1;
  132.                         s++;
  133.                         }
  134.                     if( isdigit( *s ) )
  135.                         indent = atoi( s );     /* forced indentation width */
  136.                     break;
  137.                 case 'e':
  138.                     if( keeppars < NKEEP )
  139.                         {
  140.                         int i, p;
  141.                         p = atoi( s );
  142.                         /* Sort paragraph numbers, by insertion. */
  143.                         for( i = keeppars; i>0; i-- )
  144.                             {
  145.                             if( keeppar[ i-1 ] < p )
  146.                                 keeppar[ i ] = keeppar[ i-1 ];
  147.                             else
  148.                                 break;
  149.                             }
  150.                         keeppar[ i ] = p;
  151.                         keeppars++;
  152.                         }
  153.                     else
  154.                         {
  155.                         fprintf( stderr, "too many -e's\n" );
  156.                         return 10;
  157.                         }
  158.                     break;
  159.                 case 'a': case 'A':
  160.                     if( solwords<NDELIM )
  161.                         {
  162.                         strncpy( solword[solwords], s, KEYLEN-1 );
  163.                         scmplen[ solwords++ ] = islower( s[-1] ) ? strlen( s ) : 0;
  164.                         }
  165.                     else
  166.                         {
  167.                         fprintf( stderr, "too many -a/-A's\n" );
  168.                         return 10;
  169.                         }
  170.                     break;
  171.                 case 'z': case 'Z':
  172.                     if( eolwords<NDELIM )
  173.                         {
  174.                         strncpy( eolword[eolwords], s, KEYLEN-1 );
  175.                         ecmplen[ eolwords++ ] = islower( s[-1] ) ? strlen( s ) : 0;
  176.                         }
  177.                     else
  178.                         {
  179.                         fprintf( stderr, "too many -z/-Z's\n" );
  180.                         return 10;
  181.                         }
  182.                     break;
  183.                 case '?':
  184.                     help( NULL );
  185.                     return 0;
  186.                 default:
  187.                     help(argv[0]);
  188.                     return 10;
  189.                 }
  190.             }
  191.         else
  192.             {
  193.             help( argv[0] );
  194.             return 10;
  195.             }
  196.         }
  197.     if( blank2i<0 )
  198.         {                       /* -bc without an explicit width */
  199.         if( indent>0 )
  200.             blank2i = indent;
  201.         else
  202.             blank2i = 4;
  203.         }
  204.     wordwrap();
  205.     return 0;
  206.     }
  207.  
  208.  
  209. int parno;                      /* paragraph number, based on blank lines */
  210. int lline;                      /* length of the current output line */
  211. UBYTE joinme;                   /* the last character of a hyphenated word */
  212.  
  213. void newline()
  214. /* little assistant to wordwrap(), not completely trivial */
  215.     {
  216.     int i;
  217.  
  218.     if( joinme )
  219.         {
  220.         putchar( '-' );         /* flush pending hyphen */
  221.         joinme = 0;
  222.         }
  223.     putchar( '\n' );            /* perform line feed */
  224.     lline = 0;
  225.     for( i=1; i<=omargin; i++ )
  226.         putchar( ' ' );         /* print left margin */
  227.     }
  228.  
  229.  
  230. void wordwrap()
  231. /* central function, copies from <stdin> to <stdout> */
  232.     {
  233.     int i, lword, dented, breakme;
  234.     UBYTE *s, c, word[WORDLEN];
  235.  
  236.     for( i=1; i<=omargin; i++ )
  237.         putchar(' ');           /* left margin for the first output line */
  238.     /* let's go */
  239.     parno = 0;
  240.     lline = 0;
  241.     dented = 0;
  242.     joinme = 0;
  243.     breakme = 0;
  244.     while( 1 )
  245.         {
  246.         /* Before we read anything: Maybe we have to process this */
  247.         /* paragraph in escape mode? */
  248.         if( keeppars>0 && parno == keeppar[ keeppars-1 ] )
  249.             {
  250.             /* Remove this entry from our list. */
  251.             /* We do this in a loop to defend against duplicate entries. */
  252.             /* Doing it this way is easier than not allowing duplicates */
  253.             /* at all. */
  254.             while( keeppars>0 && parno == keeppar[ keeppars-1 ] )
  255.                 keeppars--;
  256.             if( lline )
  257.                 {
  258.                 /* Characters on the current output line mean that no */
  259.                 /* -b option was supplied. But we want a blank line here. */
  260.                 if( !dented )
  261.                     newline();
  262.                 newline();
  263.                 dented = 0;
  264.                 }
  265.             scrollmode( imargin, omargin );
  266.             /* One trailing blank line after the escaped paragraph has */
  267.             /* been processed by scrollmode() itself to create a blank */
  268.             /* line. If it should also create an indentation, we'll have */
  269.             /* to take care of that now. */
  270.             if( blank2i )
  271.                 {
  272.                 if( !smart_b2i )
  273.                     while( lline < blank2i )
  274.                         {
  275.                         putchar(' ');
  276.                         lline++;
  277.                         }
  278.                 dented = 1;
  279.                 }
  280.             continue;
  281.             }
  282.         /* Now read a word. */
  283.         lword = getword( word, WORDLEN );
  284.         if( lword == 0 )
  285.             break;  /* EOF, quit */
  286.         breakme = (word[ lword-1 ] == '\n');
  287.         if( breakme )           /* "short line" break request? */
  288.             word[ --lword ] = '\0';
  289.         /* Three main cases: */
  290.         /* 1) this was a blank line */
  291.         if( lword == 0 )
  292.             {
  293.             if( blank2i )
  294.                 {
  295.                 if( lline )
  296.                     newline();
  297.                 if( !(smart_b2i && dented) )
  298.                     while( lline < blank2i )
  299.                         {
  300.                         putchar(' ');
  301.                         lline++;
  302.                         }
  303.                 dented = 1;
  304.                 }
  305.             else if( blanks )
  306.                 {
  307.                 if( lline )
  308.                     newline();
  309.                 newline();
  310.                 }
  311.             }
  312.         /* 2) this was the start of an indented line */
  313.         else if( !is_graph[ word[ 0 ] ] )
  314.             {
  315.             if( (indent || i2blank) && lword>imargin )
  316.                 {
  317.                 if( lline )
  318.                     newline();
  319.                 dented = 1;
  320.                 if( i2blank )
  321.                     newline();
  322.                 if( indent>0 )
  323.                     for( lline = 0; lline<indent; lline++ )
  324.                         putchar(' ');
  325.                 else if( indent )
  326.                     {
  327.                     printf( "%s", &word[ imargin ] );
  328.                     lline = lword-imargin;
  329.                     }
  330.                 }
  331.             }
  332.         /* 3) a regular word */
  333.         else
  334.             {
  335.             /* however, it might still be a "delimiter" word: */
  336.             /* ... one that always ends a line? */
  337.             for( i=0; i<eolwords; i++ )
  338.                 {
  339.                 s = word;
  340.                 if( ecmplen[i] && lword > ecmplen[i] )
  341.                     s += lword-ecmplen[i];
  342.                 if( strcmp( s, eolword[i] ) == 0 )
  343.                     breakme = 1;    /* treat it like a "short line" */
  344.                 }
  345.             /* ... or one that must be at the start of a line? */
  346.             for( i=0; i<solwords && lline; i++ )
  347.                 if( (scmplen[i] && strncmp( word, solword[i], scmplen[i] ) == 0 )
  348.                   || strcmp( word, solword[i] ) == 0 )
  349.                     newline();
  350.             /* and we still might have to process the "hyphen" marker: */
  351.             if( (c = word[ lword-1 ]) == '\t' )
  352.                 word[ --lword ] = '\0';
  353.             /* now, finally, print the word: */
  354.             if( lline == 0 || dented )
  355.                 {               /* at the start of the line */
  356.                 printf("%s", word);
  357.                 lline += lword;
  358.                 dented = 0;
  359.                 }
  360.             else
  361.                 {               /* (attempt to) append to an existing line */
  362.                 if( unhyphen<2 && is_lower[ joinme ] && is_lower[ word[0] ]
  363.                   && lline+lword <= lmax )
  364.                     ;           /* this will join a previously hyphenated word */
  365.                 else if( joinme && is_alnum[word[0]] && lline+lword < lmax )
  366.                     {           /* join a word, preserving the hyphen */
  367.                     putchar('-');
  368.                     lline++;
  369.                     }
  370.                 else
  371.                     {
  372.                     if( joinme )
  373.                         {
  374.                         putchar('-');
  375.                         lline++;
  376.                         joinme = 0;
  377.                         }
  378.                     if( lline+lword < lmax )
  379.                         {       /* standard case: insert a space between words */
  380.                         putchar(' ');
  381.                         lline++;
  382.                         }
  383.                     else        /* or start a new line */
  384.                         newline();
  385.                     }
  386.                 printf("%s", word);
  387.                 lline += lword;
  388.                 }
  389.             /* hyphen marker processing, part 2: */
  390.             if( c=='\t' )
  391.                 joinme = word[ lword-1 ];
  392.             else
  393.                 joinme = 0;
  394.             /* pending "short line" break? */
  395.             if( breakme )
  396.                 newline();
  397.             }
  398.         }
  399.         if( lline )
  400.             {
  401.             if( joinme )
  402.                 putchar( '-' );
  403.             putchar( '\n' );
  404.             }
  405.     }
  406.  
  407.  
  408.  
  409. /*-----------------------------------------------------------------------*\
  410.   A filtering frontend for getchar(), which silently drops some stuff
  411.   that getword() does not want to see, and does some conversions.
  412.     - ANSI escapes (stuff starting with "\e[" or 0x9b, ending with a
  413.       character >='@') are dropped
  414.     - printing characters as well as ' ', '\t', '\n' go unharmed
  415.     - non-breakable space (0xa0) may be converted to ' ' if requested
  416.     - '\r' and '\v' are converted to ' '
  417.     - '\f' is converted to '\n'
  418.     - all other non-printing characters (including DEL) are dropped
  419.   Note that getword() assumes that this is built on top of getchar() and
  420.   nothing else, as it calls ungetc( stdin ) directly at some point.
  421. \*-----------------------------------------------------------------------*/
  422.  
  423. int get_char()
  424.     {
  425.     int c;
  426.  
  427. loop:
  428.     c = getchar();
  429. check:
  430.     if( c == 0x7f )             /* DEL */
  431.         goto loop;
  432.     if( c == 0xa0 && wrapnbsp )
  433.         return ' ';             /* make nbsp breakable */
  434.     if( c == EOF || (c & 0x60) )
  435.         return c;
  436.     switch( c )
  437.         {
  438.         case '\t':
  439.         case '\n':
  440.             return c;
  441.         case '\v':
  442.         case '\r':
  443.             return ' ';
  444.         case '\f':
  445.             return '\n';
  446.         case '\e':
  447.             c = getchar();
  448.             if( c != '[' )
  449.                 goto check;
  450.             /* got "\e[", drop through to 0x9b */
  451.         case 0x9b:
  452.             do  {
  453.                 c = getchar();
  454.                 }
  455.             while( c != EOF && c < '@' );
  456.         }
  457.     goto loop;
  458.     }
  459.  
  460.  
  461. /*-----------------------------------------------------------------------*\
  462.   Copies one word of the input stream to s[] (return value is strlen(s)),
  463.   then skips all subsequent spaces, stopping at the next word or EOL.
  464.     - will return "\n" for an empty line
  465.     - will return a string of spaces for a line starting with blanks
  466.     - will append " " to a word ending a sentence (if widespace != 0)
  467.     - will replace a trailing hyphen by "\t", (if at EOL and unhyphen != 0)
  468.     - will append "\n" to the last word on a "short" line
  469.     - will return an empty string at EOF
  470.   Note that the combinations " \n" and "\t\n" may very well occur,
  471.   whereas combinations of " " and "\t" won't.
  472.   Reading an empty line will also bump the paragraph counter <parno>,
  473.   *if* it was the first blank line after a regular one.
  474. \*-----------------------------------------------------------------------*/
  475.  
  476. int getword( UBYTE s[], int lim )
  477.     {
  478.     int c, j, i = 0;
  479.     static int nonblanks = 0;
  480.     static int inbetween = 1;   /* between paragraphs? */
  481.  
  482.     c = get_char();
  483.     if( c == EOF )
  484.         {
  485.         s[ 0 ] = '\0';
  486.         return 0;
  487.         }
  488.     if( !is_graph[ c ] )        /* This can only be at the start of a line, */
  489.         {                       /* -> indented (or even a blank) line. */
  490.         while( !is_graph[ c ] && c != '\n' )
  491.             {
  492.             j = ( c == '\t' ) ? tabsize : 1;
  493.             while( j-- )
  494.                 if( i<lim-1 )
  495.                     s[ i++ ] = ' ';
  496.             c = get_char();
  497.             }
  498.         if( c == '\n' || c == EOF )
  499.             {
  500.             i = 0;
  501.             s[ i++ ] = '\n';    /* blank line */
  502.             }
  503.         }
  504.     else
  505.         {                       /* read a word of non-blanks */
  506.         while( is_graph[ c ] )
  507.             {
  508.             if( i<lim-1 )
  509.                 s[i++] = c;
  510.             else
  511.                 {
  512.                 fprintf( stderr, "warning: long input word (> %d chars)\n", lim );
  513.                 break;
  514.                 }
  515.             c = get_char();
  516.             }
  517.         nonblanks += i;
  518.         /* skip blanks after the word */
  519.         while( !is_graph[ c ] && c != '\n' )
  520.             c = get_char();
  521.         if( widespace )         /* check for end of sentence */
  522.             if( i>2 && is_alnum[ s[ i-3 ] ]
  523.              && !(i == 3 && is_cons[s[i-3]] && is_cons[s[i-2]]) )
  524.                 /* (makes sure that words like "g.", "e.g." and "Dr."
  525.                    cannot create extra wide space) */
  526.                 switch( s[ i-1 ] )
  527.                     {
  528.                     case '.': case '!': case '?':
  529.                         s[i++] = ' ';
  530.                         break;
  531.                     case ':':
  532.                         if( widespace>1 )
  533.                             s[i++] = ' ';
  534.                         break;
  535.                     }
  536.         if( c == '\n' )         /* Just read the last word on this line */
  537.             {
  538.             /* did we read the first half of a split word? */
  539.             if( unhyphen && i>1 && s[ i-1 ] == '-' && is_alnum[ s[ i-2 ] ] )
  540.                 s[i-1] = '\t';
  541.             /* was this a "short line"? */
  542.             if( nonblanks <= shortchars )
  543.                 s[i++] = '\n';
  544.             nonblanks = 0;
  545.             }
  546.         }
  547.     if( is_graph[ c ] )         /* did we stop on a non-blank character? */
  548.         ungetc( c, stdin );
  549.     s[ i ] = '\0';
  550.  
  551.     if( *s == '\n' )            /* maybe bump the paragraph counter */
  552.         {
  553.         if( !inbetween )
  554.             parno++;
  555.         inbetween = 1;
  556.         }
  557.     else
  558.         inbetween = 0;
  559.  
  560.     return i;
  561.     }
  562.  
  563.  
  564.  
  565. /*-----------------------------------------------------------------------*\
  566.        Copy a paragraph line by line, only adjusting the left margin.
  567.               Stops after reading (and printing) a blank line,
  568.    but not before processing at least one line containing printable text.
  569.     To be consistent with what the wordwrap() body does, we will assume
  570.     that the left margin has already been set for our first line, and we
  571.                     will set a left margin when we quit.
  572.        Calling this function will always bump the paragraph counter.
  573. \*-----------------------------------------------------------------------*/
  574.  
  575. void scrollmode( int imrg, int omrg )
  576.     {
  577.     int c, i, nonblanks = 0, anything = 0;
  578.  
  579.     fflush( stdout );
  580.     fprintf( stderr, "\e[2m" ); /* highlight console output */
  581.     fflush( stderr );
  582.  
  583.     while( (c = get_char()) != EOF )
  584.         {
  585.         /* Margin stripping. */
  586.         i = imrg;
  587.         while( i > 0 && c != EOF && c != '\n' && !is_graph[ c ] )
  588.             {
  589.             i -= ( c == '\t' ) ? tabsize : 1;
  590.             c = get_char();
  591.             }
  592.         /* Usually we'll now have i=0, but for example stripping three */
  593.         /* spaces of one tab might leave us with i=-1. In that case the */
  594.         /* following code would produce one space. */
  595.         while( i++ < 0 )
  596.             putchar(' ');
  597.  
  598.         /* Copy a line. */
  599.         nonblanks = 0;
  600.         while( c != '\n' && c != EOF )
  601.             {
  602.             putchar( c );
  603.             if( is_graph[ c ] )
  604.                 nonblanks++;
  605.             c = get_char();
  606.             }
  607.         newline();              /* this also creates a left margin */
  608.  
  609.         /* Was this a blank line, and should we quit? */
  610.         if( nonblanks )
  611.             anything = 1;
  612.         else if( anything )
  613.             break;
  614.         }
  615.  
  616.     fflush( stdout );
  617.     fprintf( stderr, "\e[0m" ); /* normalize console output */
  618.     fflush( stderr );
  619.  
  620.     parno++;
  621.     }
  622.  
  623.  
  624.  
  625. /*-----------------------------------------------------------------------*\
  626.                  Set up some tables to classify characters,
  627.                  assuming 8 bit ECMA / ISO Latin-1 charset.
  628. \*-----------------------------------------------------------------------*/
  629.  
  630. void init_tabs()
  631.     {
  632.     int c;
  633.  
  634.     /* Start by filling all tables with "FALSE". */
  635.     for( c=0; c<256; c++ )
  636.         {
  637.         is_graph[ c ] = 0;
  638.         is_alnum[ c ] = 0;
  639.         is_lower[ c ] = 0;
  640.         is_cons[ c ] = 0;
  641.         }
  642.  
  643.     /* Printable, non-blank characters: */
  644.     for( c=33; c<127; c++ )     /* skip sp (32) and del (127) */
  645.         is_graph[ c ] = 1;
  646.     for( c=160; c<256; c++ )    /* we consider nbsp (160) non-blank! */
  647.         is_graph[ c ] = 1;
  648.  
  649.     /* Alphanumeric characters: */
  650.     for( c='a'; c<='z'; c++ )   /* standard ASCII */
  651.         is_alnum[ c ] = 1;
  652.     for( c='A'; c<='Z'; c++ )
  653.         is_alnum[ c ] = 1;
  654.     for( c='0'; c<='9'; c++ )
  655.         is_alnum[ c ] = 1;
  656.     for( c=192; c<256; c++ )    /* ECMA stuff */
  657.         is_alnum[ c ] = 1;
  658.     is_alnum[ 247 ] = 0;
  659.     is_alnum[ 215 ] = 0;        /* "÷", "×" */
  660.  
  661.     /* Lowercase letters: */
  662.     for( c='a'; c<='z'; c++ )
  663.         is_lower[ c ] = 1;
  664.     for( c=224; c<256; c++ )
  665.         is_lower[ c ] = 1;
  666.     is_lower[ 247 ] = 0;        /* "÷" */
  667.     is_lower[ 223 ] = 1;        /* "ß" */
  668.  
  669.     /* Consonants: */
  670.     for( c='a'; c<='z'; c++ )   /* standard ASCII */
  671.         switch(c)
  672.             {
  673.             case 'a': case 'e': case 'i':
  674.             case 'o': case 'u': case 'y':
  675.                 break;
  676.             default:
  677.                 is_cons[ c ] = 1;
  678.             }
  679.     for( c=224; c<256; c++ )    /* ECMA characters */
  680.         switch(c)
  681.             {
  682.             case 231: case 240: /* "ç", "ð", "ñ", "þ" */
  683.             case 241: case 254:
  684.                 is_cons[ c ] = 1;
  685.                 break;
  686.             default:
  687.                 ;
  688.             }
  689.     for( c=32; c<256; c++ )     /* the corresponding uppercase letters */
  690.         if( is_cons[ c ] )
  691.             is_cons[ c-32 ] = 1;
  692.     is_cons[ 223 ] = 1;         /* "ß" */
  693.     }
  694.  
  695.